/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "session_impl.h"
#include "channel_impl.h"
#include "loghelper.h"
#include "reader_impl.h"
#include "se_common_exception.h"
#include "service_manager.h"
#include "secure_element_agent.h"
#include "service_impl.h"
#include "se_hianalytics.h"

namespace OHOS {
namespace SE {
SessionImpl::SessionImpl(wptr<ReaderImpl> reader)
    : boundReader_(reader), sessionClosed_(false)
{
    InfoLog("SessionImpl::SessionImpl");
}

SessionImpl::~SessionImpl()
{
    InfoLog("SessionImpl::~SessionImpl");
}

ErrorCode SessionImpl::GetATR(std::string& atr)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (sessionClosed_ || boundReader_.promote() == nullptr) {
        ErrorLog("GetATR sessionClosed_ or boundReader_ is null");
        atr = std::string();
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }
    atr = boundReader_.promote()->GetAtr();
    return ErrorCode::SE_SUCCESS;
}

ErrorCode SessionImpl::Close(void)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (sessionClosed_) {
        ErrorLog("Close sessionClosed_ already.");
        return ErrorCode::SE_SUCCESS;
    }
    if (boundReader_.promote() == nullptr) {
        ErrorLog("Close SE_ILLEGAL_STATE_ERROR");
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }

    // close all channel if session is closed.
    boundReader_.promote()->GetSecureElementAgent().lock()->CloseChannels(CommonUtils::GetUniqueTag());
    if (sessionOpenChannels_.empty()) {
        WarnLog("Close sessionOpenChannels_ is empty.");
    } else {
        sessionOpenChannels_.clear();
    }

    sessionClosed_ = true;
    SeHiAnalytics::GetInstance().SeEventLog(LOG_SESSION_IS_CLOSED, LOG_DEFAULT_STR);
    InfoLog("SessionImpl::Close done.");
    return ErrorCode::SE_SUCCESS;
}

ErrorCode SessionImpl::IsClosed(bool& isClosed)
{
    std::lock_guard<std::mutex> lock(mutex_);
    isClosed = sessionClosed_;
    return ErrorCode::SE_SUCCESS;
}

ErrorCode SessionImpl::CloseChannels(void)
{
    InfoLog("SessionImpl::CloseChannels");
    std::lock_guard<std::mutex> lock(mutex_);
    if (sessionClosed_) {
        WarnLog("OpenBasicChannel sessionClosed_ already.");
        return ErrorCode::SE_SUCCESS;
    }
    if (boundReader_.promote() == nullptr) {
        ErrorLog("OpenBasicChannel sessionClosed_(%{public}d) or boundReader_ is null", sessionClosed_);
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }
    boundReader_.promote()->GetSecureElementAgent().lock()->CloseChannels(CommonUtils::GetUniqueTag());
    InfoLog("SessionImpl::CloseChannels Done");
    sessionOpenChannels_.clear();
    return ErrorCode::SE_SUCCESS;
}

ErrorCode SessionImpl::OpenBasicChannel(const std::string& aid,
                                        uint8_t p2,
                                        sptr<OMAPI::ISecureElementChannel>& channelIface, std::string &apduResp)
{
    InfoLog("SessionImpl::OpenBasicChannel aid = %{public}s, p2 = %{public}d", aid.c_str(), p2);
    std::lock_guard<std::mutex> lock(mutex_);
    if (sessionClosed_ || boundReader_.promote() == nullptr) {
        ErrorLog("OpenBasicChannel sessionClosed_(%{public}d) or boundReader_ is null", sessionClosed_);
        throw IllegalStateError("OpenBasicChannel Session is closed");
    }

    if (p2 != 0x00 && p2 != 0x04 && p2 != 0x08 && p2 != 0x0C) {
        throw UnsupportedOperationError("OpenBasicChannel p2 not supported", "");
    }

    channelIface = boundReader_.promote()->GetSecureElementAgent().lock()->OpenBasicChannel(aid, p2, apduResp);
    if (channelIface) {
        sessionOpenChannels_.push_back(channelIface);
    } else {
        ErrorLog("OpenBasicChannel rcvd failed");
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }
    return ErrorCode::SE_SUCCESS;
}

ErrorCode SessionImpl::OpenLogicalChannel(const std::string& aid,
                                          uint8_t p2,
                                          sptr<OMAPI::ISecureElementChannel>& channelIface, std::string &apduResp)
{
    InfoLog("SessionImpl::OpenLogicalChannel aid = %{public}s, p2 = %{public}d", aid.c_str(), p2);
    std::lock_guard<std::mutex> lock(mutex_);
    if (sessionClosed_ || boundReader_.promote() == nullptr) {
        ErrorLog("OpenLogicalChannel sessionClosed_(%{public}d) or boundReader_ is null", sessionClosed_);
        throw IllegalStateError("OpenLogicalChannel Session is closed");
    }

    if (p2 != 0x00 && p2 != 0x04 && p2 != 0x08 && p2 != 0x0C) {
        throw UnsupportedOperationError("OpenLogicalChannel p2 not supported", "");
    }

    channelIface = boundReader_.promote()->GetSecureElementAgent().lock()->OpenLogicalChannel(aid, p2, apduResp);
    if (channelIface) {
        sessionOpenChannels_.push_back(channelIface);
    } else {
        ErrorLog("OpenLogicalChannel rcvd failed");
        return ErrorCode::SE_ILLEGAL_STATE_ERROR;
    }
    return ErrorCode::SE_SUCCESS;
}

void SessionImpl::RemoveChannel(wptr<OMAPI::ISecureElementChannel> channel)
{
    InfoLog("SessionImpl::RemoveChannel unimpl.");
}
}  // namespace SE
}  // namespace OHOS
